﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using RapidDownloader.Model.Services;
using Vidyano;
using Vidyano.Model;
using Vidyano.Services;
using Vidyano.Windows;

namespace RapidDownloader.Model
{
    public class DownloadContext : ViewModelBase, IContextProvider
    {
        // TODO: Remember settings

        #region Fields

        public static readonly AsyncWriter<DownloadFile> AsyncWriter = new AsyncWriter<DownloadFile>(f => f.SaveFileName);

        private bool _CanPasteLinksFromClipboard = true, _CanClear;
        private static string defaultDownloadFolder = KnownFolders.Downloads ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Downloads");
        private string _DownloadFolder = defaultDownloadFolder;
        private readonly ConcurrentDictionary<string, DownloadFile> filesDictionary = new ConcurrentDictionary<string, DownloadFile>();

        private static readonly MessageDialog clearFilesDialog = new MessageDialog
                                                                     {
                                                                         Message = "Are you sure you want to clear the list of download links?",
                                                                         Icon = MessageBoxIcon.Question,
                                                                         Buttons = new[] { "Clear list", "Don't clear list" },
                                                                         DefaultButton = 0,
                                                                     };

        private static readonly IDownloadProvider[] providers = new IDownloadProvider[] { new Services.RapidShare.RapidShareDownloadProvider(), new Services.HotFile.HotFileDownloadProvider() };

        #endregion

        #region Constructors

        public DownloadContext()
        {
            InvalidateCanDownload = new BehaviorSubject<DownloadContext>(this);
            Subscriptions.Subscribe(PropertyChanged.Where(e => e.PropertyName == "Files"), FilesChanged);

            PasteLinksFromClipboard();
        }

        #endregion

        #region Properties

        public bool CanPasteLinksFromClipboard
        {
            get
            {
                return _CanPasteLinksFromClipboard;
            }
            private set
            {
                _CanPasteLinksFromClipboard = value;
                SendPropertyChanged("CanPasteLinksFromClipboard");
            }
        }

        public bool CanClear
        {
            get
            {
                return _CanClear;
            }
            private set
            {
                if (_CanClear != value)
                {
                    _CanClear = value;
                    SendPropertyChanged("CanClear");
                }
            }
        }

        public DownloadFile[] Files
        {
            get
            {
                return filesDictionary.Values.OrderBy(f => f.FileName).ToArray();
            }
        }

        public string DownloadFolder
        {
            get
            {
                return _DownloadFolder;
            }
            set
            {
                if (_DownloadFolder != value)
                {
                    _DownloadFolder = value;
                    defaultDownloadFolder = value;
                    SendPropertyChanged("DownloadFolder");

                    UpdateSaveFileNames(Files);
                    InvalidateCanDownload.OnNext(this);
                }
            }
        }

        public ISubject<DownloadContext> InvalidateCanDownload { get; private set; }

        #endregion

        #region Command Methods

        public void PasteLinksFromClipboard()
        {
            // Gets the files from the clipboard
            var files = GetFiles(ServiceLocator.GetService<IClipboardService>().GetText());
            if (files.Length == 0)
                return;

            var newFiles = new List<DownloadFile>();

            var currentFileCount = filesDictionary.Count;
            files.Run(file =>
                          {
                              if (filesDictionary.TryAdd(file.Key, file))
                                  newFiles.Add(file);
                              else
                                  file.Dispose();
                          });
            UpdateSaveFileNames(Files);

            // Only do this when there are actually new files to check
            if (filesDictionary.Count > currentFileCount)
            {
                Task.Factory.StartNew(() =>
                {
                    try
                    {
                        CanPasteLinksFromClipboard = false;

                        // Check all new files
                        providers.Run(p => p.CheckFiles(newFiles));
                    }
                    finally
                    {
                        CanPasteLinksFromClipboard = true;
                    }
                }).ContinueWith(t => SendPropertyChanged("Files"));
            }
        }

        public void Clear(IEnumerable selectedFiles)
        {
            if (selectedFiles != null)
            {
                var files = selectedFiles.Cast<DownloadFile>();
                if (files.Any())
                {
                    files.Run(f =>
                        {
                            DownloadFile removed;
                            filesDictionary.TryRemove(f.Key, out removed);
                            removed.Dispose();
                        });

                    SendPropertyChanged("Files");
                    return;
                }
            }

            if (clearFilesDialog.Show() == 0)
            {
                filesDictionary.Clear();
                SendPropertyChanged("Files");
            }
        }

        public void BrowseForDownloadFolder()
        {
            var folder = ServiceLocator.GetService<IDialogService>().ShowBrowseFolder(DownloadFolder, "Target folder");
            if (!string.IsNullOrWhiteSpace(folder))
                DownloadFolder = folder;
        }

        #endregion

        #region Private Methods

        private void FilesChanged(PropertyChangedEventArgs e)
        {
            CanClear = Files.Length > 0;
            InvalidateCanDownload.OnNext(this);
        }

        private void UpdateSaveFileNames(IEnumerable<DownloadFile> files)
        {
            if (!string.IsNullOrEmpty(DownloadFolder))
                files.Run(f => f.SaveFileName = Path.Combine(DownloadFolder, f.FileName));
        }

        #endregion

        #region IContextProvider Implementation

        void IContextProvider.Add<TEntity>(TEntity entity)
        {
            throw new NotSupportedException();
        }

        IQueryable<TEntity> IContextProvider.CreateQuery<TEntity>()
        {
            throw new NotSupportedException();
        }

        void IContextProvider.Delete<TEntity>(TEntity entity)
        {
            throw new NotSupportedException();
        }

        bool IContextProvider.HasChanges
        {
            get { return false; }
        }

        bool IContextProvider.IsDisposed
        {
            get { return true; }
        }

        void IContextProvider.SaveChanges()
        {
            throw new NotSupportedException();
        }

        bool IContextProvider.SupportsChanges
        {
            get { return false; }
        }

        void IDisposable.Dispose()
        {
            filesDictionary.Values.Run(f => f.Dispose());
            filesDictionary.Clear();
        }

        #endregion

        #region Helper Methods

        private static DownloadFile[] GetFiles(string value)
        {
#if DEBUG
            // Read urls from file while debugging the application
            try { value = File.ReadAllText(@"C:\Temp\RapidUrls.txt"); }
            catch { }
#endif

            return providers.SelectMany(p => p.GetFiles(value)).ToArray();
        }

        #endregion
    }
}